﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.Direct3D10;
using System.Collections;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Représente une collection ordonnée d'objets qui peuvent être transformés en buffer DirectX10. 
    /// </summary>
    public class BufferableCollection<T> : Freezable, IList<T>
    {
        #region Constructors
        /// <summary>
        /// Crée une nouvelle instance de la classe <see cref="BufferableCollection{T}"/>
        /// </summary>
        /// <param name="bufferUsage">Type de buffer cible</param>
        public BufferableCollection(BindFlags bufferUsage)
        { 
            _flags = bufferUsage;
        }
        #endregion

        #region Data
        private readonly IList<T> _list = new List<T>();
        private readonly BindFlags _flags;
        private SlimDX.Direct3D10.Buffer _buffer;
        #endregion

        #region Public Methods
        /// <summary>
        /// <see cref="SlimDX.Direct3D10.Buffer"/> de la collection
        /// </summary>
        /// <exception cref="InvalidOperationException">Soulevée si la collection n'est pas dans l'état Frozen.</exception>
        public SlimDX.Direct3D10.Buffer Buffer
        {
            get
            {
                if (!IsFrozen) throw new InvalidOperationException("Collection must be frozen before getting buffer");
                return _buffer;
            }
        }
        #endregion

        #region Freezable Overrides
        /// <summary>
        /// Rend l'objet <see cref="Freezable"/> non modifiable ou vérifie si celui-ci peut être rendu non modifiable ou pas.
        /// </summary>
        /// <param name="isChecking">true pour retourner une indication de la possibilité ou non de figer l'objet (sans le figer réellement) ; false pour figer réellement l'objet.</param>
        /// <returns>
        /// Si <paramref name="isChecking"/> est true, cette méthode retourne true si le <see cref="Freezable"/> peut être rendu non modifiable, ou false si cette opération est impossible.
        /// Si <paramref name="isChecking"/> est false, cette méthode retourne true si le <see cref="Freezable"/> spécifié est désormais non modifiable, ou false si cette opération est impossible.
        /// </returns>
        protected override bool FreezeCore(bool isChecking)
        {
            bool returnValue = base.FreezeCore(isChecking);
            if (!isChecking && this.Count != 0)
            {
                _buffer = this.CreateBuffer(_flags);
            }
            return returnValue;
        }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new BufferableCollection<T>(_flags);
        }
        #endregion

        #region IList<T> Implementation
        /// <summary>
        /// Recherche l'objet spécifié et retourne l'index de base zéro de la première occurrence dans l'ensemble du <see cref="BufferableCollection{T}" />.
        /// </summary>
        /// <param name="item">
        /// Objet à trouver dans <see cref="BufferableCollection{T}" />.
        /// La valeur peut être null pour les types référence.
        /// </param>
        /// <returns>Index de base zéro de la première occurrence de item dans l'ensemble du <see cref="BufferableCollection{T}" />, s'il existe , sinon -1.</returns>
        public int IndexOf(T item)
        {
            return _list.IndexOf(item);
        }

        /// <summary>
        /// Insère un élément dans <see cref="BufferableCollection{T}" /> à l'index spécifié.
        /// </summary>
        /// <param name="index">Index de base zéro auquel <paramref name="item"/> doit être inséré.</param>
        /// <param name="item">
        /// Objet à insérer.
        /// La valeur peut être null pour les types référence.
        /// </param>
        public void Insert(int index, T item)
        {
            _list.Insert(index, item);
        }

        /// <summary>
        /// Supprime l'élément au niveau de l'index spécifié de <see cref="BufferableCollection{T}" />.
        /// </summary>
        /// <param name="index">Index de base zéro de l'élément à supprimer.</param>
        public void RemoveAt(int index)
        {
            _list.RemoveAt(index);
        }

        /// <summary>
        /// Obtient ou définit l'élément situé à l'index spécifié.
        /// </summary>
        /// <param name="index">Index de base zéro de l'élément à obtenir ou définir.</param>
        /// <returns>Élément au niveau de l'index spécifié.</returns>
        public T this[int index]
        {
            get { return _list[index]; }
            set { _list[index] = value; }
        }

        /// <summary>
        /// Ajoute un objet à la fin de <see cref="BufferableCollection{T}" />.
        /// </summary>
        /// <param name="item">
        /// Objet à ajouter à la fin de <see cref="BufferableCollection{T}" />.
        /// La valeur peut être null pour les types référence.
        /// </param>
        public void Add(T item)
        {
            _list.Add(item);
        }

        /// <summary>
        /// Supprime tous les éléments de <see cref="BufferableCollection{T}" />.
        /// </summary>
        public void Clear()
        {
            _list.Clear();
        }

        /// <summary>
        /// Détermine si un élément est dans <see cref="BufferableCollection{T}" />.
        /// </summary>
        /// <param name="item">Objet à trouver dans <see cref="BufferableCollection{T}" />.La valeur peut être null pour les types référence.</param>
        /// <returns>true si <paramref name="item"/> existe dans <see cref="BufferableCollection{T}" /> ; sinon, false.</returns>
        public bool Contains(T item)
        {
            return _list.Contains(item);
        }

        /// <summary>
        /// Copie l'ensemble du <see cref="BufferableCollection{T}" /> vers un tableau compatible unidimensionnel, en commençant à l'index spécifié du tableau cible.
        /// </summary>
        /// <param name="array">
        /// <see cref="Array"/> unidimensionnel qui constitue la destination des éléments copiés à partir de <see cref="BufferableCollection{T}" />.
        /// <see cref="Array"/> doit avoir une indexation de base zéro.
        /// </param>
        /// <param name="arrayIndex">Index de base zéro dans <paramref name="array"/> au niveau duquel commence la copie.</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            _list.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Obtient le nombre d'éléments réellement contenus dans <see cref="BufferableCollection{T}" />
        /// </summary>
        public int Count
        {
            get { return _list.Count; }
        }
        
        /// <summary>
        /// Supprime la première occurrence d'un objet spécifique de <see cref="BufferableCollection{T}" />.
        /// </summary>
        /// <param name="item">Objet à supprimer de <see cref="BufferableCollection{T}" />.La valeur peut être null pour les types référence.</param>
        /// <returns>
        /// true si la suppression de <paramref name="item"/> est réussie ; sinon, false.
        /// Cette méthode retourne également false si item est introuvable dans <see cref="BufferableCollection{T}" />.
        /// </returns>
        public bool Remove(T item)
        {
            return _list.Remove(item);
        }

        /// <summary>
        /// Retourne un énumérateur qui itère au sein de <see cref="BufferableCollection{T}" />.
        /// </summary>
        /// <returns><see cref="List{T}.Enumerator"/> pour <see cref="BufferableCollection{T}" />.</returns>
        public IEnumerator<T> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        bool ICollection<T>.IsReadOnly
        {
            get { return _list.IsReadOnly; }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        #endregion
    }
}
